﻿using log4net;
using Mathmall.Administration.Authorization;
using Mathmall.Administration.Models;
using Mathmall.Administration.Option;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Mathmall.Administration.Data;
using Microsoft.EntityFrameworkCore.Internal;

namespace Mathmall.Administration.Helper
{
    public class JwtHelper
    {
        private readonly IOptionsSnapshot<JwtOption> _jwtOptionsSnapshot;
        private static readonly ILog _logger = LogManager.GetLogger(typeof(JwtHelper));
        private readonly AdministrationContext _administrationContext;

        public JwtHelper(IOptionsSnapshot<JwtOption> jwtOptionsSnapshot, AdministrationContext administrationContext)
        {
            _jwtOptionsSnapshot = jwtOptionsSnapshot;
            _administrationContext = administrationContext;
        }

        /// <summary>
        /// 生成token
        /// </summary>
        /// <param name="user"></param>
        /// <param name="roleIds"></param>
        /// <returns></returns>
        // public string CreateToken(User user)
        public string CreateToken(User user, List<int> roleIds)
        {
            // 将查询到的角色放入claims
            List<Claim> claims = new List<Claim>();
            foreach (var roleId in roleIds)
            {
                claims.Add(new Claim(ClaimTypes.Role, roleId.ToString()));
            }

            claims.Add(new Claim(ClaimTypes.Name, user.UserName!));
            claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));

            string issuerSigningKey = _jwtOptionsSnapshot.Value.IssuerSigningKey;
            // 构建jwt
            DateTime expiresMinutes = DateTime.Now.AddSeconds(_jwtOptionsSnapshot.Value.ExpiresMinutes);
            SymmetricSecurityKey symmetricSecurityKey =
                new SymmetricSecurityKey(Encoding.UTF8.GetBytes(issuerSigningKey));
            SigningCredentials signingCredentials =
                new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature);
            JwtSecurityToken securityToken = new JwtSecurityToken(claims: claims, expires: expiresMinutes,
                signingCredentials: signingCredentials);
            string jwtString = new JwtSecurityTokenHandler().WriteToken(securityToken);

            return jwtString;
        }

        /// <summary>
        /// 解析jwt,获取用户权限
        /// </summary>
        /// <param name="jwtString"></param>
        /// <returns></returns>
        public IQueryable<Menu?> JwtParser(string jwtString)
        {
            // 获取jwt中的payload
            string[] jwtStringArray = jwtString.Split('.');
            string payload = jwtStringArray[1];
            // 解析payload，转换成可读的文字
            payload = payload.Replace('-', '+').Replace('_', '/');
            switch (payload.Length % 4)
            {
                case 2:
                    payload += "==";
                    break;
                case 3:
                    payload += "=";
                    break;
            }

            Byte[] bytes = Convert.FromBase64String(payload);
            JsonNode jsonNode = JsonNode.Parse(Encoding.UTF8.GetString(bytes))!;

            // 从payload中拿到用户的角色id集合
            string[] roleArray = jsonNode[ClaimTypes.Role].ToString()
                .Replace("[", "").Replace("]", "")
                .Replace("\"", "").Split(",");

            if (roleArray.Length > 0)
            {
                // 将id集合转换为int数组
                int[] roleIds = Array.ConvertAll<string, int>(roleArray, int.Parse);
                // 查询用户权限
                IQueryable<Menu> menus = from roleMenu in _administrationContext.RoleMenu
                    where roleIds.Contains(roleMenu.RoleId)
                    join menu in _administrationContext.Menus on roleMenu.MenuId equals menu.MenuID
                    select menu;
                return menus;
            }

            return null;
        }
    }
}